/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu 
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.hx.marshall;

import com.acooly.core.utils.Strings;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.provider.hx.HxConstants;
import com.acooly.module.openapi.client.provider.hx.HxProperties;
import com.acooly.module.openapi.client.provider.hx.domain.HxApiMessage;
import com.acooly.module.openapi.client.provider.hx.domain.HxRequest;
import com.acooly.module.openapi.client.provider.hx.enums.HxServiceEnum;
import com.acooly.module.openapi.client.provider.hx.support.HxKeyStoreInfo;
import com.acooly.module.openapi.client.provider.hx.utils.SignUtils;
import com.acooly.module.openapi.client.provider.hx.utils.StringHelper;
import com.acooly.module.safety.exception.SignatureVerifyException;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignerFactory;

import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;

import lombok.extern.slf4j.Slf4j;

import static com.acooly.module.openapi.client.provider.hx.utils.StringHelper.asXml;

/**
 * @author zhangpu
 */
@Slf4j
public class HxMarshallSupport {

    @Autowired
    protected HxProperties hxProperties;

    @Autowired
    protected SignerFactory signerFactory;

    @Autowired
    private KeyLoadManager keyStoreLoadManager;

    protected HxProperties getProperties() {
        return hxProperties;
    }

    protected String doMarshall(HxRequest source) {
        doVerifyParam(source);
        hxProperties.setPartnerId(source.getPartner());
        return getRequestMessage(source);
    }

    /**
     * 签名 优先获取传进来的，如果传进来没有则用配置文件中的
     *
     * @param source
     * @return
     */
    protected String getRequestMessage(HxRequest source) {
        HxKeyStoreInfo hxKeyStoreInfo = getHxKeyStoreInfo(source.getPartner());
        try {
            String reqStr;
            if(HxServiceEnum.hxNetbankPay.code().equals(source.getService())){
                // TODO
                return  "";
            }else{
                String reqMsg = source.obj2Str(source).replaceAll("\r|\n", "").replaceAll(">  <", "><").replaceAll("  ", "").replaceAll("&lt;", "<").replaceAll("&gt;", ">");
                Document doc = DocumentHelper.parseText(reqMsg);
                Element rootElt = doc.getRootElement();
                Element bodyElement = null;
                Element headElement = null;
                if(HxServiceEnum.hxWithdraw.code().equals(source.getService())){
                    bodyElement = rootElt.element("Body");
                    headElement = rootElt.element("Head");
                    Element detailsElement = bodyElement.element("IssuedDetails");
                    Element detailElement = detailsElement.element("Detail");
                    String waitSignStr = asXml(detailElement);
                    //去掉多余的字符
                    waitSignStr = waitSignStr.replaceAll("<Detail>", "");
                    waitSignStr = waitSignStr.replaceAll("</Detail>", "");
                    log.info("待签字符串：{}",waitSignStr);
                    String signStr = SignUtils.encrypt3DES(waitSignStr, hxKeyStoreInfo.getDeskey(),hxKeyStoreInfo.getDesiv());
                    detailElement.detach();
                    detailsElement.addElement("Detail").setText(signStr);
                    //利用body+数字证书做MD5签名
                    Element signature = headElement.element("Signature");
                    String tranMessageBody = asXml(bodyElement);
                    String signatureStr = DigestUtils.md5Hex(StringHelper.getBytes(tranMessageBody  + hxKeyStoreInfo.getMD5Key(),"UTF-8"));
                    signature.setText(signatureStr);
                }else if(HxServiceEnum.hxNetbankPayQuery.code().equals(source.getService())){
                    Element orderQueryReq = rootElt.element("OrderQueryReq");
                    headElement = orderQueryReq.element("head");
                    bodyElement = orderQueryReq.element("body");
                    //利用body+数字证书做MD5签名
                    Element signature = headElement.element("Signature");
                    String tranMessageBody = asXml(bodyElement);
                    String signatureStr = DigestUtils.md5Hex(StringHelper.getBytes(tranMessageBody + source.getPartner() + hxKeyStoreInfo.getMD5Key(),"UTF-8"));
                    signature.setText(signatureStr);
                }else{
                    Element issuedTradeReq = rootElt.element("IssuedTradeReq");
                    headElement = issuedTradeReq.element("head");
                    bodyElement = issuedTradeReq.element("body");
                    //利用body+数字证书做MD5签名
                    Element signature = headElement.element("Signature");
                    String tranMessageBody = asXml(bodyElement);
                    String signatureStr = DigestUtils.md5Hex(StringHelper.getBytes(tranMessageBody + source.getPartner() + hxKeyStoreInfo.getMD5Key(),"UTF-8"));
                    signature.setText(signatureStr);
                }
                reqStr = rootElt.asXML();
            }
            return reqStr;
        } catch (Exception e) {
            throw new ApiClientException("组装请求报文失败：" + e.getMessage());
        }
    }


    /**
     * 校验参数
     *
     * @param source
     */
    protected void doVerifyParam(HxApiMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }


    protected void beforeMarshall(HxApiMessage message) {
        if(Strings.isBlank(message.getPartnerId())) {
            message.setPartnerId(getProperties().getPartnerId());
        }
    }


    /**
     * 验签
     *
     * @param plain
     * @return
     */
    protected void doVerifySign(String plain, String partnerId,String serviceName) {
            HxKeyStoreInfo hxKeyStoreInfo = getHxKeyStoreInfo(partnerId);
        try {
            Document doc = DocumentHelper.parseText(plain);
            Element rootElt = doc.getRootElement();
            Element infoElement = null;
            Element bodyElement;
            if(HxServiceEnum.hxWithdraw.code().equals(serviceName)){
                infoElement = rootElt.element("Head");
                bodyElement = rootElt.element("Body");
            }else if(HxServiceEnum.hxNetbankPayQuery.code().equals(serviceName)){
                Element orderQueryRsp = rootElt.element("OrderQueryRsp");
                infoElement = orderQueryRsp.element("head");
                bodyElement = orderQueryRsp.element("body");
            }else{
                Element issuedTradeRsp = rootElt.element("IssuedTradeRsp");
                infoElement = issuedTradeRsp.element("head");
                bodyElement = issuedTradeRsp.element("body");
            }
            Element signStrElement = infoElement.element("Signature");
            String signature = signStrElement.getText();
            String waitSignStr = bodyElement.asXML();
            String result = "";
            if(HxServiceEnum.hxWithdraw.code().equals(serviceName)){
                result = DigestUtils.md5Hex(StringHelper.getBytes(waitSignStr + hxKeyStoreInfo.getMD5Key(), "UTF-8"));
            } else {
                result = DigestUtils.md5Hex(StringHelper.getBytes(waitSignStr + partnerId + hxKeyStoreInfo.getMD5Key(), "UTF-8"));
            }
            if (signature == null || result == null || !signature.equals(result)) {
                throw new RuntimeException("签名未通过");
            }
        }catch (Exception e) {
            throw new SignatureVerifyException(e.getMessage());
        }
    }

    /**
     * 获取keyStoreInfo
     *
     * @return
     */
    protected HxKeyStoreInfo getHxKeyStoreInfo(String partnerId) {
        HxKeyStoreInfo hxKeyStoreInfo;
        try {
            hxKeyStoreInfo = keyStoreLoadManager.load(partnerId, HxConstants.PROVIDER_NAME);
        } catch (Exception e) {
            synchronized (this) {
                log.info("商户partnerId={}密钥加载失败，启用配置文件密钥",partnerId);
                hxKeyStoreInfo = new HxKeyStoreInfo();
                hxKeyStoreInfo.setDeskey(hxProperties.getDeskey());
                hxKeyStoreInfo.setDesiv(hxProperties.getDesiv());
                hxKeyStoreInfo.setMD5Key(hxProperties.getMD5Key());
            }
        }
        return hxKeyStoreInfo;
    }

}
